/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010-2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _OBJECT_H
#define _OBJECT_H

#include "Common.h"
#include "UpdateFields.h"
#include "UpdateData.h"
#include "GridReference.h"
#include "ObjectDefines.h"
#include "GridDefines.h"
#include "Map.h"

#include <set>
#include <string>
#include <sstream>

#define CONTACT_DISTANCE            0.5f
#define INTERACTION_DISTANCE        5.0f
#define ATTACK_DISTANCE             5.0f
#define MAX_VISIBILITY_DISTANCE     500.0f // max distance for visible objects
#define SIGHT_RANGE_UNIT            50.0f
#define DEFAULT_VISIBILITY_DISTANCE 90.0f // default visible distance, 90 yards on continents
#define DEFAULT_VISIBILITY_INSTANCE 120.0f // default visible distance in instances, 120 yards
#define DEFAULT_VISIBILITY_BGARENAS 180.0f // default visible distance in BG/Arenas, 180 yards
#define DEFAULT_WORLD_OBJECT_SIZE   0.388999998569489f      // player size, also currently used (correctly?) for any non Unit world objects
#define DEFAULT_COMBAT_REACH        1.5f
#define MIN_MELEE_REACH             2.0f
#define NOMINAL_MELEE_RANGE         5.0f
#define MELEE_RANGE                 (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players
enum TypeMask {
	TYPEMASK_OBJECT = 0x00000001,
	TYPEMASK_ITEM = 0x00000002,
	TYPEMASK_CONTAINER = 0x00000006, // TYPEMASK_ITEM | 0x0004
	TYPEMASK_UNIT = 0x00000008, //creature or player
	TYPEMASK_PLAYER = 0x00000010,
	TYPEMASK_GAMEOBJECT = 0x00000020,
	TYPEMASK_DYNAMICOBJECT = 0x00000040,
	TYPEMASK_CORPSE = 0x00000080,
	TYPEMASK_IN_GUILD = 0x00010000, //only player with guild
	TYPEMASK_SEER = TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT
};

enum TypeID {
	TYPEID_OBJECT = 0,
	TYPEID_ITEM = 1,
	TYPEID_CONTAINER = 2,
	TYPEID_UNIT = 3,
	TYPEID_PLAYER = 4,
	TYPEID_GAMEOBJECT = 5,
	TYPEID_DYNAMICOBJECT = 6,
	TYPEID_CORPSE = 7
};

#define NUM_CLIENT_OBJECT_TYPES             8

uint32 GuidHigh2TypeId(uint32 guid_hi);

enum TempSummonType {
	TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 1, // despawns after a specified time OR when the creature disappears
	TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN = 2, // despawns after a specified time OR when the creature dies
	TEMPSUMMON_TIMED_DESPAWN = 3, // despawns after a specified time
	TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT = 4, // despawns after a specified time after the creature is out of combat
	TEMPSUMMON_CORPSE_DESPAWN = 5, // despawns instantly after death
	TEMPSUMMON_CORPSE_TIMED_DESPAWN = 6, // despawns after a specified time after death
	TEMPSUMMON_DEAD_DESPAWN = 7, // despawns when the creature disappears
	TEMPSUMMON_MANUAL_DESPAWN = 8
// despawns when UnSummon() is called
};

enum PhaseMasks {
	PHASEMASK_NORMAL = 0x00000001, PHASEMASK_ANYWHERE = 0xFFFFFFFF
};

enum NotifyFlags {
	NOTIFY_NONE = 0x00,
	NOTIFY_AI_RELOCATION = 0x01,
	NOTIFY_VISIBILITY_CHANGED = 0x02,
	NOTIFY_ALL = 0xFF
};

class WorldPacket;
class UpdateData;
class ByteBuffer;
class WorldSession;
class Creature;
class Player;
class UpdateMask;
class InstanceScript;
class GameObject;
class TempSummon;
class Vehicle;
class CreatureAI;
class ZoneScript;
class Unit;
class Transport;

typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;

class Object {
public:
	virtual ~Object();

	const bool& IsInWorld() const {
		return m_inWorld;
	}
	virtual void AddToWorld() {
		if (m_inWorld)
			return;

		ASSERT(m_uint32Values);

		m_inWorld = true;

		// synchronize values mirror with values array (changes will send in updatecreate opcode any way
		ClearUpdateMask(true);
	}
	virtual void RemoveFromWorld() {
		if (!m_inWorld)
			return;

		m_inWorld = false;

		// if we remove from world then sending changes not required
		ClearUpdateMask(true);
	}

	const uint64& GetGUID() const {
		return GetUInt64Value(0);
	}
	uint32 GetGUIDLow() const {
		return GUID_LOPART(GetUInt64Value(0));
	}
	uint32 GetGUIDMid() const {
		return GUID_ENPART(GetUInt64Value(0));
	}
	uint32 GetGUIDHigh() const {
		return GUID_HIPART(GetUInt64Value(0));
	}
	const ByteBuffer& GetPackGUID() const {
		return m_PackGUID;
	}
	uint32 GetEntry() const {
		return GetUInt32Value(OBJECT_FIELD_ENTRY);
	}
	void SetEntry(uint32 entry) {
		SetUInt32Value(OBJECT_FIELD_ENTRY, entry);
	}

	TypeID GetTypeId() const {
		return m_objectTypeId;
	}
	bool isType(uint16 mask) const {
		return (mask & m_objectType);
	}

	virtual void BuildCreateUpdateBlockForPlayer(UpdateData *data,
			Player *target) const;
	void SendUpdateToPlayer(Player* player);

	void BuildValuesUpdateBlockForPlayer(UpdateData *data,
			Player *target) const;
	void BuildOutOfRangeUpdateBlock(UpdateData *data) const;
	void BuildMovementUpdateBlock(UpdateData * data, uint32 flags = 0) const;

	virtual void DestroyForPlayer(Player *target, bool anim = false) const;

	const int32& GetInt32Value(uint16 index) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		return m_int32Values[index];
	}

	const uint32& GetUInt32Value(uint16 index) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		return m_uint32Values[index];
	}

	const uint64& GetUInt64Value(uint16 index) const {
		ASSERT(index + 1 < m_valuesCount || PrintIndexError(index , false));
		return *((uint64*) &(m_uint32Values[index]));
	}

	const float& GetFloatValue(uint16 index) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		return m_floatValues[index];
	}

	uint8 GetByteValue(uint16 index, uint8 offset) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		ASSERT(offset < 4);
		return *(((uint8*) &m_uint32Values[index]) + offset);
	}

	uint16 GetUInt16Value(uint16 index, uint8 offset) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		ASSERT(offset < 2);
		return *(((uint16*) &m_uint32Values[index]) + offset);
	}

	void SetInt32Value(uint16 index, int32 value);
	void SetUInt32Value(uint16 index, uint32 value);
	void UpdateUInt32Value(uint16 index, uint32 value);
	void SetUInt64Value(uint16 index, const uint64 &value);
	void SetFloatValue(uint16 index, float value);
	void SetByteValue(uint16 index, uint8 offset, uint8 value);
	void SetUInt16Value(uint16 index, uint8 offset, uint16 value);
	void SetInt16Value(uint16 index, uint8 offset, int16 value) {
		SetUInt16Value(index, offset, (uint16) value);
	}
	void SetStatFloatValue(uint16 index, float value);
	void SetStatInt32Value(uint16 index, int32 value);

	bool AddUInt64Value(uint16 index, const uint64 &value);
	bool RemoveUInt64Value(uint16 index, const uint64 &value);

	void ApplyModUInt32Value(uint16 index, int32 val, bool apply);
	void ApplyModInt32Value(uint16 index, int32 val, bool apply);
	void ApplyModUInt64Value(uint16 index, int32 val, bool apply);
	void ApplyModPositiveFloatValue(uint16 index, float val, bool apply);
	void ApplyModSignedFloatValue(uint16 index, float val, bool apply);

	void ApplyPercentModFloatValue(uint16 index, float val, bool apply) {
		float value = GetFloatValue(index);
		ApplyPercentModFloatVar(value, val, apply);
		SetFloatValue(index, value);
	}

	void SetFlag(uint16 index, uint32 newFlag);
	void RemoveFlag(uint16 index, uint32 oldFlag);

	void ToggleFlag(uint16 index, uint32 flag) {
		if (HasFlag(index, flag))
			RemoveFlag(index, flag);
		else
			SetFlag(index, flag);
	}

	bool HasFlag(uint16 index, uint32 flag) const {
		if (index >= m_valuesCount && !PrintIndexError(index, false))
			return false;
		return (m_uint32Values[index] & flag) != 0;
	}

	void SetByteFlag(uint16 index, uint8 offset, uint8 newFlag);
	void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);

	void ToggleFlag(uint16 index, uint8 offset, uint8 flag) {
		if (HasByteFlag(index, offset, flag))
			RemoveByteFlag(index, offset, flag);
		else
			SetByteFlag(index, offset, flag);
	}

	bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		ASSERT(offset < 4);
		return (((uint8*) &m_uint32Values[index])[offset] & flag) != 0;
	}

	void ApplyModFlag(uint16 index, uint32 flag, bool apply) {
		if (apply)
			SetFlag(index, flag);
		else
			RemoveFlag(index, flag);
	}

	void SetFlag64(uint16 index, uint64 newFlag) {
		uint64 oldval = GetUInt64Value(index);
		uint64 newval = oldval | newFlag;
		SetUInt64Value(index, newval);
	}

	void RemoveFlag64(uint16 index, uint64 oldFlag) {
		uint64 oldval = GetUInt64Value(index);
		uint64 newval = oldval & ~oldFlag;
		SetUInt64Value(index, newval);
	}

	void ToggleFlag64(uint16 index, uint64 flag) {
		if (HasFlag64(index, flag))
			RemoveFlag64(index, flag);
		else
			SetFlag64(index, flag);
	}

	bool HasFlag64(uint16 index, uint64 flag) const {
		ASSERT(index < m_valuesCount || PrintIndexError(index , false));
		return (GetUInt64Value(index) & flag) != 0;
	}

	void ApplyModFlag64(uint16 index, uint64 flag, bool apply) {
		if (apply)
			SetFlag64(index, flag);
		else
			RemoveFlag64(index, flag);
	}

	void ClearUpdateMask(bool remove);

	bool LoadValues(const char* data);

	uint16 GetValuesCount() const {
		return m_valuesCount;
	}

	virtual bool hasQuest(uint32 /* quest_id */) const {
		return false;
	}
	virtual bool hasInvolvedQuest(uint32 /* quest_id */) const {
		return false;
	}
	virtual void BuildUpdate(UpdateDataMapType&) {
	}
	void BuildFieldsUpdate(Player *, UpdateDataMapType &) const;

	// FG: some hacky helpers
	void ForceValuesUpdateAtIndex(uint32);

	Player* ToPlayer() {
		if (GetTypeId() == TYPEID_PLAYER)
			return reinterpret_cast<Player*>(this);
		else
			return NULL;
	}
	const Player* ToPlayer() const {
		if (GetTypeId() == TYPEID_PLAYER)
			return (const Player*) ((Player*) this);
		else
			return NULL;
	}
	Creature* ToCreature() {
		if (GetTypeId() == TYPEID_UNIT)
			return reinterpret_cast<Creature*>(this);
		else
			return NULL;
	}
	const Creature* ToCreature() const {
		if (GetTypeId() == TYPEID_UNIT)
			return (const Creature*) ((Creature*) this);
		else
			return NULL;
	}

	Unit* ToUnit() {
		if (GetTypeId() == TYPEID_UNIT || GetTypeId() == TYPEID_PLAYER)
			return reinterpret_cast<Unit*>(this);
		else
			return NULL;
	}
	const Unit* ToUnit() const {
		if (GetTypeId() == TYPEID_UNIT || GetTypeId() == TYPEID_PLAYER)
			return (const Unit*) ((Unit*) this);
		else
			return NULL;
	}
	GameObject* ToGameObject() {
		if (GetTypeId() == TYPEID_GAMEOBJECT)
			return reinterpret_cast<GameObject*>(this);
		else
			return NULL;
	}
	const GameObject* ToGameObject() const {
		if (GetTypeId() == TYPEID_GAMEOBJECT)
			return (const GameObject*) ((GameObject*) this);
		else
			return NULL;
	}
protected:

	Object();

	void _InitValues();
	void _Create(uint32 guidlow, uint32 entry, HighGuid guidhigh);
	std::string _ConcatFields(uint16 startIndex, uint16 size) const;
	void _LoadIntoDataField(const char* data, uint32 startOffset, uint32 count);

	virtual void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
	virtual void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
	void _BuildMovementUpdate(ByteBuffer * data, uint16 flags) const;
	void _BuildValuesUpdate(uint8 updatetype, ByteBuffer *data,
			UpdateMask *updateMask, Player *target) const;

	uint16 m_objectType;

	TypeID m_objectTypeId;
	uint16 m_updateFlag;

	union {
		int32 *m_int32Values;
		uint32 *m_uint32Values;
		float *m_floatValues;
	};

	uint32 *m_uint32Values_mirror;

	uint16 m_valuesCount;

	bool m_objectUpdated;

private:
	bool m_inWorld;

	ByteBuffer m_PackGUID;

	// for output helpfull error messages from asserts
	bool PrintIndexError(uint32 index, bool set) const;
	Object(const Object&); // prevent generation copy constructor
	Object& operator=(Object const&); // prevent generation assigment operator
};

struct Position {
	struct PositionXYZStreamer {
		explicit PositionXYZStreamer(Position& pos) :
				m_pos(&pos) {
		}
		Position* m_pos;
	};

	struct PositionXYZOStreamer {
		explicit PositionXYZOStreamer(Position& pos) :
				m_pos(&pos) {
		}
		Position* m_pos;
	};

	float m_positionX;
	float m_positionY;
	float m_positionZ;
	float m_orientation;

	void Relocate(float x, float y) {
		m_positionX = x;
		m_positionY = y;
	}
	void Relocate(float x, float y, float z) {
		m_positionX = x;
		m_positionY = y;
		m_positionZ = z;
	}
	void Relocate(float x, float y, float z, float orientation) {
		m_positionX = x;
		m_positionY = y;
		m_positionZ = z;
		m_orientation = orientation;
	}
	void Relocate(const Position &pos) {
		m_positionX = pos.m_positionX;
		m_positionY = pos.m_positionY;
		m_positionZ = pos.m_positionZ;
		m_orientation = pos.m_orientation;
	}
	void Relocate(const Position *pos) {
		m_positionX = pos->m_positionX;
		m_positionY = pos->m_positionY;
		m_positionZ = pos->m_positionZ;
		m_orientation = pos->m_orientation;
	}
	void RelocateOffset(const Position &offset);
	void SetOrientation(float orientation) {
		m_orientation = orientation;
	}

	float GetPositionX() const {
		return m_positionX;
	}
	float GetPositionY() const {
		return m_positionY;
	}
	float GetPositionZ() const {
		return m_positionZ;
	}
	float GetOrientation() const {
		return m_orientation;
	}

	void GetPosition(float &x, float &y) const {
		x = m_positionX;
		y = m_positionY;
	}
	void GetPosition(float &x, float &y, float &z) const {
		x = m_positionX;
		y = m_positionY;
		z = m_positionZ;
	}
	void GetPosition(float &x, float &y, float &z, float &o) const {
		x = m_positionX;
		y = m_positionY;
		z = m_positionZ;
		o = m_orientation;
	}
	void GetPosition(Position *pos) const {
		if (pos)
			pos->Relocate(m_positionX, m_positionY, m_positionZ, m_orientation);
	}

	Position::PositionXYZStreamer PositionXYZStream() {
		return Position::PositionXYZStreamer(*this);
	}
	Position::PositionXYZOStreamer PositionXYZOStream() {
		return Position::PositionXYZOStreamer(*this);
	}

	bool IsPositionValid() const;

	float GetExactDist2dSq(float x, float y) const {
		float dx = m_positionX - x;
		float dy = m_positionY - y;
		return dx * dx + dy * dy;
	}
	float GetExactDist2d(const float x, const float y) const {
		return sqrt(GetExactDist2dSq(x, y));
	}
	float GetExactDist2dSq(const Position *pos) const {
		float dx = m_positionX - pos->m_positionX;
		float dy = m_positionY - pos->m_positionY;
		return dx * dx + dy * dy;
	}
	float GetExactDist2d(const Position *pos) const {
		return sqrt(GetExactDist2dSq(pos));
	}
	float GetExactDistSq(float x, float y, float z) const {
		float dz = m_positionZ - z;
		return GetExactDist2dSq(x, y) + dz * dz;
	}
	float GetExactDist(float x, float y, float z) const {
		return sqrt(GetExactDistSq(x, y, z));
	}
	float GetExactDistSq(const Position *pos) const {
		float dx = m_positionX - pos->m_positionX;
		float dy = m_positionY - pos->m_positionY;
		float dz = m_positionZ - pos->m_positionZ;
		return dx * dx + dy * dy + dz * dz;
	}
	float GetExactDist(const Position *pos) const {
		return sqrt(GetExactDistSq(pos));
	}

	void GetPositionOffsetTo(const Position & endPos,
			Position & retOffset) const;

	float GetAngle(const Position *pos) const;
	float GetAngle(float x, float y) const;
	float GetRelativeAngle(const Position *pos) const {
		return GetAngle(pos) - m_orientation;
	}
	float GetRelativeAngle(float x, float y) const {
		return GetAngle(x, y) - m_orientation;
	}
	void GetSinCos(float x, float y, float &vsin, float &vcos) const;

	bool IsInDist2d(float x, float y, float dist) const {
		return GetExactDist2dSq(x, y) < dist * dist;
	}
	bool IsInDist2d(const Position *pos, float dist) const {
		return GetExactDist2dSq(pos) < dist * dist;
	}
	bool IsInDist(float x, float y, float z, float dist) const {
		return GetExactDistSq(x, y, z) < dist * dist;
	}
	bool IsInDist(const Position *pos, float dist) const {
		return GetExactDistSq(pos) < dist * dist;
	}
	bool HasInArc(float arcangle, const Position *pos) const;
	bool HasInLine(const Unit *target, float distance, float width) const;
	std::string ToString() const;
};
ByteBuffer &operator>>(ByteBuffer& buf,
		Position::PositionXYZOStreamer const & streamer);
ByteBuffer & operator<<(ByteBuffer& buf,
		Position::PositionXYZStreamer const & streamer);
ByteBuffer &operator>>(ByteBuffer& buf,
		Position::PositionXYZStreamer const & streamer);
ByteBuffer & operator<<(ByteBuffer& buf,
		Position::PositionXYZOStreamer const & streamer);

struct MovementInfo {
	// common
	uint64 guid;
	uint32 flags;
	uint16 flags2;
	Position pos;
	uint32 time;
	// transport
	uint64 t_guid;
	Position t_pos;
	uint32 t_time;
	uint32 t_time2;
	int8 t_seat;
	// swimming/flying
	float pitch;
	// falling
	uint32 fallTime;
	// jumping
	float j_zspeed, j_sinAngle, j_cosAngle, j_xyspeed;
	// spline
	float splineElevation;

	MovementInfo() {
		pos.Relocate(0, 0, 0, 0);
		guid = 0;
		flags = 0;
		flags2 = 0;
		time = t_time = t_time2 = fallTime = 0;
		splineElevation = 0;
		pitch = j_zspeed = j_sinAngle = j_cosAngle = j_xyspeed = 0.0f;
		t_guid = 0;
		t_pos.Relocate(0, 0, 0, 0);
		t_seat = -1;
	}

	uint32 GetMovementFlags() {
		return flags;
	}
	void AddMovementFlag(uint32 flag) {
		flags |= flag;
	}
	bool HasMovementFlag(uint32 flag) const {
		return flags & flag;
	}

	uint16 GetExtraMovementFlags() {
		return flags2;
	}
	void AddExtraMovementFlag(uint16 flag) {
		flags2 |= flag;
	}
	bool HasExtraMovementFlag(uint16 flag) const {
		return flags2 & flag;
	}

	void OutDebug();
};

#define MAPID_INVALID 0xFFFFFFFF

class WorldLocation: public Position {
public:
	explicit WorldLocation(uint32 _mapid = MAPID_INVALID, float _x = 0,
			float _y = 0, float _z = 0, float _o = 0) :
			m_mapId(_mapid) {
		Relocate(_x, _y, _z, _o);
	}
	WorldLocation(const WorldLocation &loc) {
		WorldRelocate(loc);
	}

	void WorldRelocate(const WorldLocation &loc) {
		m_mapId = loc.GetMapId();
		Relocate(loc);
	}
	uint32 GetMapId() const {
		return m_mapId;
	}

	uint32 m_mapId;
};

template<class T>
class GridObject {
public:
	GridReference<T> &GetGridRef() {
		return m_gridRef;
	}
protected:
	GridReference<T> m_gridRef;
};

template<class T_VALUES, class T_FLAGS, class FLAG_TYPE, uint8 ARRAY_SIZE>
class FlaggedValuesArray32 {
public:
	FlaggedValuesArray32() {
		memset(&m_values, 0x00, sizeof(T_VALUES) * ARRAY_SIZE);
		m_flags = 0;
	}

	T_FLAGS GetFlags() const {
		return m_flags;
	}
	bool HasFlag(FLAG_TYPE flag) const {
		return m_flags & (1 << flag);
	}
	void AddFlag(FLAG_TYPE flag) {
		m_flags |= (1 << flag);
	}
	void DelFlag(FLAG_TYPE flag) {
		m_flags &= ~(1 << flag);
	}

	T_VALUES GetValue(FLAG_TYPE flag) const {
		return m_values[flag];
	}
	void SetValue(FLAG_TYPE flag, T_VALUES value) {
		m_values[flag] = value;
	}
	void AddValue(FLAG_TYPE flag, T_VALUES value) {
		m_values[flag] += value;
	}

private:
	T_VALUES m_values[ARRAY_SIZE];
	T_FLAGS m_flags;
};

class WorldObject: public Object, public WorldLocation {
protected:
	explicit WorldObject();
public:
	virtual ~WorldObject();

	virtual void Update(uint32 /*time_diff*/) {
	}

	void _Create(uint32 guidlow, HighGuid guidhigh, uint32 phaseMask);

	virtual void RemoveFromWorld() {
		if (!IsInWorld())
			return;

		DestroyForNearbyPlayers();

		Object::RemoveFromWorld();
	}

	void GetNearPoint2D(float &x, float &y, float distance,
			float absAngle) const;
	void GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z,
			float searcher_size, float distance2d, float absAngle) const;
	void GetClosePoint(float &x, float &y, float &z, float size,
			float distance2d = 0, float angle = 0) const {
		// angle calculated from current orientation
		GetNearPoint(NULL, x, y, z, size, distance2d, GetOrientation() + angle);
	}
	void MovePosition(Position &pos, float dist, float angle);
	void GetNearPosition(Position &pos, float dist, float angle) {
		GetPosition(&pos);
		MovePosition(pos, dist, angle);
	}
	void MovePositionToFirstCollision(Position &pos, float dist, float angle);
	void GetFirstCollisionPosition(Position &pos, float dist, float angle) {
		GetPosition(&pos);
		MovePositionToFirstCollision(pos, dist, angle);
	}
	void GetRandomNearPosition(Position &pos, float radius) {
		GetPosition(&pos);
		MovePosition(pos, radius * (float) rand_norm(),
				(float) rand_norm() * static_cast<float>(2 * M_PI));
	}

	void GetContactPoint(const WorldObject* obj, float &x, float &y, float &z,
			float distance2d = CONTACT_DISTANCE) const {
		// angle to face `obj` to `this` using distance includes size of `obj`
		GetNearPoint(obj, x, y, z, obj->GetObjectSize(), distance2d,
				GetAngle(obj));
	}

	float GetObjectSize() const {
		return (m_valuesCount > UNIT_FIELD_COMBATREACH) ?
				m_floatValues[UNIT_FIELD_COMBATREACH] :
				DEFAULT_WORLD_OBJECT_SIZE;
	}
	void UpdateGroundPositionZ(float x, float y, float &z) const;

	void GetRandomPoint(const Position &srcPos, float distance, float &rand_x,
			float &rand_y, float &rand_z) const;
	void GetRandomPoint(const Position &srcPos, float distance,
			Position &pos) const {
		float x, y, z;
		GetRandomPoint(srcPos, distance, x, y, z);
		pos.Relocate(x, y, z, GetOrientation());
	}

	uint32 GetInstanceId() const {
		return m_InstanceId;
	}

	virtual void SetPhaseMask(uint32 newPhaseMask, bool update);
	uint32 GetPhaseMask() const {
		return m_phaseMask;
	}
	bool InSamePhase(WorldObject const* obj) const {
		return InSamePhase(obj->GetPhaseMask());
	}
	bool InSamePhase(uint32 phasemask) const {
		return (GetPhaseMask() & phasemask);
	}

	uint32 GetZoneId() const;
	uint32 GetAreaId() const;
	void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const;

	InstanceScript* GetInstanceScript();

	const char* GetName() const {
		return m_name.c_str();
	}
	void SetName(const std::string& newname) {
		m_name = newname;
	}

	virtual const char* GetNameForLocaleIdx(
			LocaleConstant /*locale_idx*/) const {
		return GetName();
	}

	float GetDistance(const WorldObject *obj) const {
		float d = GetExactDist(obj) - GetObjectSize() - obj->GetObjectSize();
		return d > 0.0f ? d : 0.0f;
	}
	float GetDistance(const Position &pos) const {
		float d = GetExactDist(&pos) - GetObjectSize();
		return d > 0.0f ? d : 0.0f;
	}
	float GetDistance(float x, float y, float z) const {
		float d = GetExactDist(x, y, z) - GetObjectSize();
		return d > 0.0f ? d : 0.0f;
	}
	float GetDistance2d(const WorldObject* obj) const {
		float d = GetExactDist2d(obj) - GetObjectSize() - obj->GetObjectSize();
		return d > 0.0f ? d : 0.0f;
	}
	float GetDistance2d(float x, float y) const {
		float d = GetExactDist2d(x, y) - GetObjectSize();
		return d > 0.0f ? d : 0.0f;
	}
	float GetDistanceZ(const WorldObject* obj) const;

	bool IsInMap(const WorldObject* obj) const {
		if (obj)
			return IsInWorld() && obj->IsInWorld()
					&& (GetMap() == obj->GetMap()) && InSamePhase(obj);
		else
			return false;
	}
	bool IsWithinDist3d(float x, float y, float z, float dist) const {
		return IsInDist(x, y, z, dist + GetObjectSize());
	}
	bool IsWithinDist3d(const Position *pos, float dist) const {
		return IsInDist(pos, dist + GetObjectSize());
	}
	bool IsWithinDist2d(float x, float y, float dist) const {
		return IsInDist2d(x, y, dist + GetObjectSize());
	}
	bool IsWithinDist2d(const Position *pos, float dist) const {
		return IsInDist2d(pos, dist + GetObjectSize());
	}
	bool _IsWithinDist(WorldObject const* obj, float dist2compare,
			bool is3D) const;
	// use only if you will sure about placing both object at same map
	bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D =
			true) const {
		return obj && _IsWithinDist(obj, dist2compare, is3D);
	}
	bool IsWithinDistInMap(WorldObject const* obj, float dist2compare,
			bool is3D = true) const {
		return obj && IsInMap(obj) && _IsWithinDist(obj, dist2compare, is3D);
	}
	bool IsWithinLOS(float x, float y, float z) const;
	bool IsWithinLOSInMap(const WorldObject* obj) const;
	bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2,
			bool is3D = true) const;
	bool IsInRange(WorldObject const* obj, float minRange, float maxRange,
			bool is3D = true) const;
	bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
	bool IsInRange3d(float x, float y, float z, float minRange,
			float maxRange) const;
	bool isInFront(WorldObject const* target, float distance,
			float arc = M_PI) const;
	bool isInBack(WorldObject const* target, float distance,
			float arc = M_PI) const;

	bool IsInBetween(const WorldObject *obj1, const WorldObject *obj2,
			float size = 0) const;

	virtual void CleanupsBeforeDelete(bool finalCleanup = true); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units

	virtual void SendMessageToSet(WorldPacket *data, bool self) {
		SendMessageToSetInRange(data, GetVisibilityRange(), self);
	}
	virtual void SendMessageToSetInRange(WorldPacket *data, float dist,
			bool self);
	virtual void SendMessageToSet(WorldPacket *data,
			Player const* skipped_rcvr);

	virtual uint8 getLevelForTarget(WorldObject const* /*target*/) const {
		return 1;
	}

	void MonsterSay(const char* text, uint32 language, uint64 TargetGuid);
	void MonsterYell(const char* text, uint32 language, uint64 TargetGuid);
	void MonsterTextEmote(const char* text, uint64 TargetGuid,
			bool IsBossEmote = false);
	void MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper =
			false);
	void MonsterSay(int32 textId, uint32 language, uint64 TargetGuid);
	void MonsterYell(int32 textId, uint32 language, uint64 TargetGuid);
	void MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote =
			false);
	void MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper =
			false);
	void MonsterYellToZone(int32 textId, uint32 language, uint64 TargetGuid);
	void BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text,
			uint32 language, char const* name, uint64 TargetGuid) const;

	void PlayDistanceSound(uint32 sound_id, Player* target = NULL);
	void PlayDirectSound(uint32 sound_id, Player* target = NULL);

	void SendObjectDeSpawnAnim(uint64 guid);

	virtual void SaveRespawnTime() {
	}
	void AddObjectToRemoveList();

	virtual bool isValid() const;

	virtual bool isAlwaysVisibleFor(WorldObject const* /*seer*/) const {
		return false;
	}
	virtual bool canSeeAlways(WorldObject const* /*obj*/) const {
		return false;
	}
	bool canDetect(WorldObject const* obj, bool ignoreStealth) const;

	virtual bool isVisibleForInState(WorldObject const* /*seer*/) const {
		return true;
	}

	bool canDetectInvisibilityOf(WorldObject const* obj) const;
	bool canDetectStealthOf(WorldObject const* obj) const;
	virtual bool IsInvisibleDueToDespawn() const { return false; }
	virtual bool isAlwaysDetectableFor(WorldObject const* /*seer*/) const {
		return false;
	}

	float GetGridActivationRange() const;
	float GetVisibilityRange() const;
	float GetSightRange(const WorldObject* target = NULL) const;
	bool canSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false,
			bool distanceCheck = false) const;

	FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
	FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;

	FlaggedValuesArray32<int32, uint32, InvisibilityType,
			TOTAL_INVISIBILITY_TYPES> m_invisibility;
	FlaggedValuesArray32<int32, uint32, InvisibilityType,
			TOTAL_INVISIBILITY_TYPES> m_invisibilityDetect;

	FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType,
			TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibility;
	FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType,
			TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibilityDetect;

	// Low Level Packets
	void SendPlaySound(uint32 Sound, bool OnlySelf);

	virtual void SetMap(Map * map);
	virtual void ResetMap();
	Map * GetMap() const {
		ASSERT(m_currMap);
		return m_currMap;
	}
	Map * FindMap() const {
		return m_currMap;
	}
	//used to check all object's GetMap() calls when object is not in world!

	//this function should be removed in nearest time...
	Map const* GetBaseMap() const;

	void SetZoneScript();
	ZoneScript * GetZoneScript() const {
		return m_zoneScript;
	}

	TempSummon* SummonCreature(uint32 id, const Position &pos,
			TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN,
			uint32 despwtime = 0, uint32 vehId = 0) const;
	TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang =
			0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN,
			uint32 despwtime = 0) {
		if (!x && !y && !z) {
			GetClosePoint(x, y, z, GetObjectSize());
			ang = GetOrientation();
		}
		Position pos = { x, y, z, ang };
		return SummonCreature(id, pos, spwtype, despwtime, 0);
	}
	GameObject* SummonGameObject(uint32 entry, float x, float y, float z,
			float ang, float rotation0, float rotation1, float rotation2,
			float rotation3, uint32 respawnTime);
	Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur,
			CreatureAI* (*GetAI)(Creature*) = NULL);

	Creature* FindNearestCreature(uint32 entry, float range, bool alive = true);
	GameObject* FindNearestGameObject(uint32 entry, float range);
	Player* FindNearestPlayer(float range, bool alive = true);
	std::list<Player*> GetNearestPlayersList(float range, bool alive = true);

	void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList,
			uint32 uiEntry, float fMaxSearchRange);
	void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList,
			uint32 uiEntry, float fMaxSearchRange);

	void DestroyForNearbyPlayers();
	virtual void UpdateObjectVisibility(bool forced = true);
	void BuildUpdate(UpdateDataMapType&);

	//relocation and visibility system functions
	void AddToNotify(uint16 f) {
		m_notifyflags |= f;
	}
	bool isNeedNotify(uint16 f) const {
		return m_notifyflags & f;
	}
	uint16 GetNotifyFlags() const {
		return m_notifyflags;
	}
	bool NotifyExecuted(uint16 f) const {
		return m_executed_notifies & f;
	}
	void SetNotified(uint16 f) {
		m_executed_notifies |= f;
	}
	void ResetAllNotifies() {
		m_notifyflags = 0;
		m_executed_notifies = 0;
	}

	bool isActiveObject() const {
		return m_isActive;
	}
	void setActive(bool isActiveObject);
	void SetWorldObject(bool apply);
	template<class NOTIFIER> void VisitNearbyObject(const float &radius,
			NOTIFIER &notifier) const {
		if (IsInWorld())
			GetMap()->VisitAll(GetPositionX(), GetPositionY(), radius,
					notifier);
	}
	template<class NOTIFIER> void VisitNearbyGridObject(const float &radius,
			NOTIFIER &notifier) const {
		if (IsInWorld())
			GetMap()->VisitGrid(GetPositionX(), GetPositionY(), radius,
					notifier);
	}
	template<class NOTIFIER> void VisitNearbyWorldObject(const float &radius,
			NOTIFIER &notifier) const {
		if (IsInWorld())
			GetMap()->VisitWorld(GetPositionX(), GetPositionY(), radius,
					notifier);
	}

#ifdef MAP_BASED_RAND_GEN
	int32 irand(int32 min, int32 max) const {return int32 (GetMap()->mtRand.randInt(max - min)) + min;}
	uint32 urand(uint32 min, uint32 max) const {return GetMap()->mtRand.randInt(max - min) + min;}
	int32 rand32() const {return GetMap()->mtRand.randInt();}
	double rand_norm() const {return GetMap()->mtRand.randExc();}
	double rand_chance() const {return GetMap()->mtRand.randExc(100.0);}
#endif

	bool m_isWorldObject;
	uint32 LastUsedScriptID;
	int32 LastDoScriptText;

	// Transports
	Transport *GetTransport() const {
		return m_transport;
	}
	virtual float GetTransOffsetX() const {
		return 0;
	}
	virtual float GetTransOffsetY() const {
		return 0;
	}
	virtual float GetTransOffsetZ() const {
		return 0;
	}
	virtual float GetTransOffsetO() const {
		return 0;
	}
	virtual uint32 GetTransTime() const {
		return 0;
	}
	virtual int8 GetTransSeat() const {
		return -1;
	}
	virtual uint64 GetTransGUID() const;
	void SetTransport(Transport *t) {
		m_transport = t;
	}

	MovementInfo m_movementInfo;
protected:
	std::string m_name;
	bool m_isActive;
	ZoneScript *m_zoneScript;

	// transports
	Transport *m_transport;

	//these functions are used mostly for Relocate() and Corpse/Player specific stuff...
	//use them ONLY in LoadFromDB()/Create() funcs and nowhere else!
	//mapId/instanceId should be set in SetMap() function!
	void SetLocationMapId(uint32 _mapId) {
		m_mapId = _mapId;
	}
	void SetLocationInstanceId(uint32 _instanceId) {
		m_InstanceId = _instanceId;
	}

private:
	Map * m_currMap; //current object's Map location

	//uint32 m_mapId;                                     // object at map with map_id
	uint32 m_InstanceId; // in map copy with instance id
	uint32 m_phaseMask; // in area phase state

	uint16 m_notifyflags;
	uint16 m_executed_notifies;
};

namespace Trinity {
template<class T>
void RandomResizeList(std::list<T> &_list, uint32 _size) {
	while (_list.size() > _size) {
		typename std::list<T>::iterator itr = _list.begin();
		advance(itr, urand(0, _list.size() - 1));
		_list.erase(itr);
	}
}

// Binary predicate to sort WorldObjects based on the distance to a reference WorldObject
class ObjectDistanceOrderPred {
public:
	ObjectDistanceOrderPred(const WorldObject *pRefObj, bool ascending = true) :
			m_refObj(pRefObj), m_ascending(ascending) {
	}
	bool operator()(const WorldObject *pLeft, const WorldObject *pRight) const {
		return m_ascending ?
				m_refObj->GetDistanceOrder(pLeft, pRight) :
				!m_refObj->GetDistanceOrder(pLeft, pRight);
	}
private:
	const WorldObject *m_refObj;
	const bool m_ascending;
};
}

#endif
